package cn.mpq.payment.common;

import cn.mpq.payment.common.Annotation.CountAopAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Aspects {

    /*
     * 定义一个切入点
     */
    @Pointcut("@annotation(cn.mpq.payment.common.Annotation.CountAopAnnotation)")
    public void myInfoAnnotation() {
    }
//
//    //匹配.cn.mpq.payment.controller包及其子包下的所有类的所有方法
//    @Pointcut("execution(* cn.mpq.payment.controller..*.*(..))")
//    public void executeService(){
//
//    }
//
//    /**
//     * 前置通知，方法调用前被调用
//     * @param joinPoint
//     */
//    @Before("executeService()")
//    public void doBeforeAdvice(JoinPoint joinPoint){
//        System.out.println("我是前置通知!!!");
//        //获取目标方法的参数信息
//        Object[] obj = joinPoint.getArgs();
//        //AOP代理类的信息
//        joinPoint.getThis();
//        //代理的目标对象
//        joinPoint.getTarget();
//        //用的最多 通知的签名
//        Signature signature = joinPoint.getSignature();
//        //代理的是哪一个方法
//        System.out.println(signature.getName());
//        //AOP代理类的名字
//        System.out.println(signature.getDeclaringTypeName());
//        //AOP代理类的类（class）信息
//        signature.getDeclaringType();
//        //获取RequestAttributes
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//        //从获取RequestAttributes中获取HttpServletRequest的信息
//        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//        //如果要获取Session信息的话，可以这样写：
//        //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
//        Enumeration<String> enumeration = request.getParameterNames();
//        Map<String,String> parameterMap = new HashMap<>();
//        while (enumeration.hasMoreElements()){
//            String parameter = enumeration.nextElement();
//            parameterMap.put(parameter,request.getParameter(parameter));
//        }
//        String str = JSON.toJSONString(parameterMap);
//        if(obj.length > 0) {
//            System.out.println("请求的参数信息为："+str);
//        }
//    }
//    /**
//     * 后置返回通知
//     * 这里需要注意的是:
//     *      如果参数中的第一个参数为JoinPoint，则第二个参数为返回值的信息
//     *      如果参数中的第一个参数不为JoinPoint，则第一个参数为returning中对应的参数
//     * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知，否则不执行，对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
//     * @param joinPoint
//     * @param keys
//     */
//    @AfterReturning(value = "execution(* cn.mpq.payment.controller..*.*(..))",returning = "keys")
//    public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
//
//        System.out.println("第一个后置返回通知的返回值："+keys);
//    }
//
//    @AfterReturning(value = "execution(* cn.mpq.payment.controller..*.*(..))",returning = "keys",argNames = "keys")
//    public void doAfterReturningAdvice2(String keys){
//
//        System.out.println("第二个后置返回通知的返回值："+keys);
//    }
//    /**
//     * 后置异常通知
//     *  定义一个名字，该名字用于匹配通知实现方法的一个参数名，当目标方法抛出异常返回后，将把目标方法抛出的异常传给通知方法；
//     *  throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知，否则不执行，
//     *      对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
//     * @param joinPoint
//     * @param exception
//     */
//    @AfterThrowing(value = "executeService()",throwing = "exception")
//    public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
//        //目标方法名：
//        System.out.println(joinPoint.getSignature().getName());
//        if(exception instanceof NullPointerException){
//            System.out.println("发生了空指针异常!!!!!");
//        }
//    }
//    /**
//     * 后置最终通知（目标方法只要执行完了就会执行后置通知方法）
//     * @param joinPoint
//     */
//    @After("executeService()")
//    public void doAfterAdvice(JoinPoint joinPoint){
//
//        System.out.println("后置通知执行了!!!!");
//    }
//
//    /**
//     * 环绕通知：
//     *   环绕通知非常强大，可以决定目标方法是否执行，什么时候执行，执行时是否需要替换方法参数，执行完毕是否需要替换返回值。
//     *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
//     */
//    @Around("execution(* cn.mpq.payment.controller..*.testAround*(..))")
//    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
//        System.out.println("环绕通知的目标方法名："+proceedingJoinPoint.getSignature().getName());
//        try {
//            Object obj = proceedingJoinPoint.proceed();
//            return obj;
//        } catch (Throwable throwable) {
//            throwable.printStackTrace();
//        }
//        return null;
//    }

    /**
     * Around（环绕通知）是在Before（前置通知）前面执行
     * &&@annotation(annotation) 这个是对方法参数的形参进行注入
     * <p>
     * value可以是多种 1纯注解形式 myInfoAnnotation()  2 混合 myInfoAnnotation()&&@annotation(annotation)&& excudeController()
     * 使用场景  1大面积使用aop  使用Pointcut来写匹配表达式  2精准定位 使用注解形式
     */
    @Around(value = "myInfoAnnotation()")
    public Object doAround(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println("方法名："+thisJoinPoint.getSignature().getName());
        // 获取切点的参数
        Object[] args = thisJoinPoint.getArgs();
        //获取自定义注解信息
        CountAopAnnotation countAopAnnotation = ((MethodSignature)thisJoinPoint.getSignature()).getMethod().getAnnotation(CountAopAnnotation.class);
        System.out.println(countAopAnnotation.value());
        //环绕通知必须执行，否则不进入注解的方法
        System.out.println("切入点执行了哟");
        return thisJoinPoint.proceed();
    }
}

